﻿using System;
using System.Collections.Generic;
using Carcassonne.Network;
using LitJson;
using Sliver.Network.Utils;
using UnityEngine;
using UnityEngine.Events;
using UnityHTTP;

namespace Sliver.Network.Http
{
    public class HttpRequest
    {
        protected readonly string method;

        protected readonly string path;

        public readonly string url;

        protected Dictionary<string, object> FormData = null;

        protected HttpRequest(string method, string path, Dictionary<string, string> urlParams = null)
        {
            this.method = method;
            this.path = path;
            url = URLHelper.BuildURL(NetworkManager.HostIP, NetworkManager.HttpPort, path, urlParams);
        }

        protected HttpRequest(string domain, string method, string path, Dictionary<string, string> urlParams = null)
        {
            this.method = method;
            this.path = path;
            url = URLHelper.BuildURL(domain, path, urlParams);
        }

        protected HttpRequest(string ip, int port, string method, string path, Dictionary<string, string> urlParams = null)
        {
            this.method = method;
            this.path = path;
            url = URLHelper.BuildURL(ip, port, path, urlParams);
        }

        public HttpRequest Form(Dictionary<string, object> formData)
        {
            FormData = formData;
            return this;
        }

        public enum RequestThread
        {
            Main,
            Current
        }

        protected RequestThread requestThread = RequestThread.Main;

        protected class ResponseMetaInfo
        {
            public RequestThread thread;
            public object callback;

            public ResponseMetaInfo(RequestThread thread, object callback)
            {
                this.thread = thread;
                this.callback = callback;
            }
        }

        protected ResponseMetaInfo onSuccess = null;

        public HttpRequest OnSuccess(UnityAction<string> callback)
        {
            onSuccess = new ResponseMetaInfo(requestThread, callback);
            return this;
        }

        public HttpRequest OnSuccessBytes(UnityAction<byte[]> callback)
        {
            onSuccess = new ResponseMetaInfo(requestThread, callback);
            return this;
        }

        public HttpRequest OnSuccessResponse(UnityAction<Response> callback)
        {
            onSuccess = new ResponseMetaInfo(requestThread, callback);
            return this;
        }

        public delegate void CallbackOnFailed(int code, string message);

        protected Tuple<RequestThread, CallbackOnFailed> onFailed = null;

        public HttpRequest OnFailed(CallbackOnFailed callback)
        {
            onFailed = new Tuple<RequestThread, CallbackOnFailed>(requestThread, callback);
            return this;
        }

        public delegate void CallbackFinally(string message);

        protected Tuple<RequestThread, CallbackFinally> onFinally = null;

        public HttpRequest Finally(CallbackFinally callback)
        {
            onFinally = new Tuple<RequestThread, CallbackFinally>(requestThread, callback);
            return this;
        }

        public HttpRequest MainThread()
        {
            requestThread = RequestThread.Main;
            return this;
        }

        public HttpRequest OnCurrentThread
        {
            get
            {
                requestThread = RequestThread.Current;
                return this;
            }
        }

        public void Send()
        {
            Request request = new Request(method, url);
            if (FormData != null)
            {
                request.Text = JsonMapper.ToJson(FormData);
            }

            request.Send((req) =>
            {
                Response response = req.response;

                int code = response == null ? 600 : response.status;
                string message = response == null ? "" : response.Text;

                switch (code)
                {
                    case 200:
                        if (onSuccess.thread == RequestThread.Main)
                        {
                            MainThreadLoop.Add("action_http_success", () => { DoOnSuccess(response, onSuccess.callback); });
                        }
                        else
                        {
                            DoOnSuccess(response, onSuccess.callback);
                        }
                        break;
                    case HttpResponseCode.Found:

                        break;
                    default:
                        if (onFailed != null)
                        {
                            if (onFailed.Item1 == RequestThread.Main)
                            {
                                MainThreadLoop.Add("action_http_failed", () => { onFailed.Item2(code, message); });
                            }
                            else
                            {
                                onFailed.Item2(code, message);
                            }
                        }

                        // Debug.Log($"{code} - {request.uri} - message: {message}");
                        break;
                }

                if (onFinally != null)
                {
                    if (onFinally.Item1 == RequestThread.Main)
                    {
                        MainThreadLoop.Add("action_http_finally", () => { onFinally.Item2(message); });
                    }
                    else
                    {
                        onFinally.Item2(message);
                    }
                }
            });
        }

        protected void DoOnSuccess(Response response, object callback)
        {
            if (callback == null) return;
            if (callback is UnityAction<string> cb1)
            {
                cb1.Invoke(response.Text);
            }
            else if (callback is UnityAction<Response> cb2)
            {
                cb2.Invoke(response);
            }
            else if (callback is UnityAction<byte[]> cb3)
            {
                cb3.Invoke(response.bytes);
            }
            else
            {
                Debug.LogWarning($"not a legal callback with type {callback.GetType()}");
            }
        }

        protected void DoOnFailed()
        {
        }

        public static HttpRequest Get(string path, Dictionary<string, string> urlParams = null)
        {
            return new HttpRequest("get", path, urlParams);
        }

        public static HttpRequest Get(string ip, int port, string path, Dictionary<string, string> urlParams = null)
        {
            return new HttpRequest(ip, port, "get", path, urlParams);
        }

        public static HttpRequest Post(string path, Dictionary<string, object> form)
        {
            return new HttpRequest("post", path, null).Form(form);
        }

        public static HttpRequest Post(string ip, int port, string path, Dictionary<string, object> form)
        {
            return new HttpRequest(ip, port, "post", path, null).Form(form);
        }

        public static HttpRequest Post(string path, Dictionary<string, string> urlParams = null)
        {
            return new HttpRequest("post", path, urlParams);
        }

        public static HttpRequest Update(string path, Dictionary<string, object> form)
        {
            return new HttpRequest("put", path).Form(form);
        }
    }

    public static class HttpResponseCode
    {
        public const int OK = 200;
        public const int MovedPermanently = 301;
        public const int Found = 302;
        public const int Forbidden = 403;
        public const int NotFound = 404;
        public const int InternalServerError = 500;
        public const int BadGateway = 502;
    }
}